
#include <map>
#include <unistd.h>
#include <google/protobuf/descriptor.h>
#include <google/protobuf/message.h>
#include "rpcclientevent.h"
#include "socket_utility.h"
#include "eventpoller.h"
#include "utility.h"
#include "meta.h"

namespace eventrpc {

  enum Request_State {
    INIT,
    WAITING_RESPONSE,
    READ_META,
    READ_MESSAGE,
  };

  struct RequestInfo {
   public:
    RequestInfo(gpb::RpcController *controller,
                gpb::Message *response,
                gpb::Closure *done)
      : controller_(controller)
        , response_(response)
        , done_(done){
        }

    gpb::RpcController *controller_;
    gpb::Message *response_;
    gpb::Closure *done_;
    string request;
  };

  struct RpcClientEvent::Impl {
   public:
    Impl(const char *ip, int port,
         RpcClientEvent *client_event,
         EventPoller *event_poller);

    ~Impl();

    int OnRead();

    int OnWrite();

    void CallMethod(const gpb::MethodDescriptor* method,
                    gpb::RpcController* controller,
                    const gpb::Message* request,
                    gpb::Message* response,
                    gpb::Closure* done);

    bool CallMethodDone(int callback_index);

   private:
    void WriteRequest(RequestInfo *request_info);

    void ReadReponse();

   private:
    RpcClientEvent *client_event_;
    EventPoller *&event_poller_;
    std::map<uint32_t, RequestInfo*> request_infos_;
    Request_State state_;
  };

  RpcClientEvent::Impl::Impl(const char *ip, int port,
                             RpcClientEvent *client_event,
                             EventPoller *event_poller)
    : client_event_(client_event)
      , event_poller_(event_poller)
      , state_(INIT) {
        client_event_->fd_ = Connect(ip, port);
      }

  RpcClientEvent::Impl::~Impl() {
  }

  int RpcClientEvent::Impl::OnRead() {
    char buf[100];

    if (state_ < WAITING_RESPONSE) {
      return -1;
    } else if (state_ == WAITING_RESPONSE) {
      state_ = READ_META;
    }

    size_t len = read(client_event_->fd_, buf, META_LEN);
    buf[len] = '\0';
    Meta meta;
    meta.Encode(buf);
    printf("method id: %d, message len: %d\n",
           meta.method_id(), meta.message_len());

    len = read(client_event_->fd_, buf, meta.message_len());
    printf("1111len: %d, read: %s\n", len, buf);
    close(client_event_->fd_);
    event_poller_->DelEvent(client_event_->event_, client_event_);

    /*
    switch (state_) {
      case READ_META:

    }
    */
    return 0;
  }

  int RpcClientEvent::Impl::OnWrite() {
    printf("onwrite\n");
    char msg[] = "hello";
    int len = 5;

    write(client_event_->fd_, msg, len);
    return 0;
  }

  void RpcClientEvent::Impl::CallMethod(const gpb::MethodDescriptor *method,
                                        gpb::RpcController *controller,
                                        const gpb::Message *request,
                                        gpb::Message *response,
                                        gpb::Closure *done) {
    RequestInfo *request_info = new RequestInfo(controller, response, done);
    Meta meta;
    meta.set_method_id(method->full_name());
    meta.set_message_len(request->ByteSize());
    request_info->request.append(meta.Decode(), META_LEN);
    printf("meta len: %d\n", request_info->request.length());
    request->AppendToString(&(request_info->request));
    printf("method: %s, method id: %d, message len: %d\n",
           method->full_name().c_str(),
           meta.method_id(), meta.message_len());
    printf("request: %s\n", request_info->request.substr(META_LEN).c_str());

    request_infos_[meta.method_id()] = request_info;
    WriteRequest(request_info);
  }

  void RpcClientEvent::Impl::WriteRequest(RequestInfo *request_info) {
    string &request = request_info->request;
    write(client_event_->fd_, request.c_str(), request.length());
    state_ = WAITING_RESPONSE;
  }

  void RpcClientEvent::Impl::ReadReponse() {
  }

  RpcClientEvent::RpcClientEvent(const char* ip, int port)
    : Event(READ_EVENT)
      , impl_(new Impl(ip, port, this, event_poller_)) {
      }

  RpcClientEvent::~RpcClientEvent() {
    delete impl_;
  }

  int RpcClientEvent::OnWrite() {
    return impl_->OnWrite();
  }

  int RpcClientEvent::OnRead() {
    return impl_->OnRead();
  }

  void RpcClientEvent::CallMethod(const gpb::MethodDescriptor *method,
                                  gpb::RpcController *controller,
                                  const gpb::Message *request,
                                  gpb::Message *response,
                                  gpb::Closure *done) {
    impl_->CallMethod(method, controller, request, response, done);
  }
};
